package com.he.hsdyc.service.impl;

import com.he.hsdyc.mapper.OrderGoodsMapper;
import com.he.hsdyc.service.MiaoShaService;
import com.he.hsdyc.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MiaoShaServiceImpl implements MiaoShaService {

    @Autowired
    private RedisService redisService;

    @Autowired
    private OrderGoodsMapper orderGoodsMapper;


    //锁超时时间，防止线程在入锁以后，无限的执行等待
    private int expireMsecs = 3 * 1000;
    //锁等待时间，防止线程饥饿
    private int timeoutMsecs = 10 * 1000;
    private volatile boolean locked = false;
    //设置锁的唯一key
    String lockKey = "good";
    //模拟初始商品数
    int n = 50;


    /***
     * 抢购代码
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean miaosha(Long goodsId) {
        try {
            //查询商品库存
//            Goods goods = orderGoodsMapper.findGoodsById(goodsId);
//            Optional.ofNullable(goods).orElseThrow(() -> new OrderException(ErrorCode.ERROR_NO_GOOD));
//            n = goods.getStock();
            if(n<=0) {
                System.out.println("手慢拍大腿,抢光啦222222222！");
//                throw  new OrderException(ErrorCode.ERROR_NO_GET2);
//                System.out.println("手慢拍大腿,抢光啦222222222！");
                return false;
            }
            if (this.lock()) {
                //修改库存
                if(n-1>=0) {
//                    orderGoodsMapper.updateStock(goodsId);
//                    if(orderGoodsMapper.findGoodsById(goodsId).getStock()>0) {
                        n--;
                        System.out.println("库存数量:"+n+"     成功!!!"+Thread.currentThread().getName() + "获得了锁");
//                    }
                }else {
                    System.out.println("手慢拍大腿,抢光啦！");
//                    throw  new OrderException(ErrorCode.ERROR_NO_GET);
//                    System.out.println("手慢拍大腿,抢光啦！");
                    return false;
                }
                this.unlock();
                return true;
            }else {
                System.out.println("换个姿势再试试");
//                throw  new OrderException(ErrorCode.ERROR_TRY_AGAIN);
//                System.out.println("换个姿势再试试");
            }


        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 为了让分布式锁的算法更稳键些，持有锁的客户端在解锁之前应该再检查一次自己的锁是否已经超时，再去做DEL操作，因为可能客户端因为某个耗时的操作而挂起，
            // 操作完的时候锁因为超时已经被别人获得，这时就不必解锁了。 ————这里没有做
            this.unlock();
        }
        return false;
    }


    public synchronized boolean lock() throws InterruptedException {

        //SETNX是将 key 的值设为 value，当且仅当 key 不存在。若给定的 key 已经存在，则 SETNX 不做任何动作。
        //GETSET key value，将给定 key 的值设为 value ，并返回 key 的旧值(old value)
        //redis是单线程的，这里的单线程指的是网络请求模块使用了一个线程（所以不需考虑并发安全性），即一个线程处理所有网络请求，其他模块仍用了多个线程


        long expires = System.currentTimeMillis() + expireMsecs + 1;
        String expiresStr = String.valueOf(expires); // 锁到期时间
        //满足下面的判断即该线程成功抢到了锁
        if (redisService.setnxByKey(lockKey, expiresStr).toString().equals("1")) {
            // lock acquired
            locked = true;
            return true;
        }
        //没有抢到锁的线程需要判断锁是否已经超时，若超时需要释放锁
        String currentValueStr = redisService.getByKey(lockKey); //当前获取锁的线程的到期时间
        //判断是否超时
        if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
            // 判断是否为空，不为空的情况下，如果被其他线程设置了值，则第二个条件判断是过不去的
            // lock is expired

            //为当前锁设置新锁到期时间，并且获取到旧锁到期时间
            String oldValueStr = redisService.getSetByKey(lockKey, expiresStr);
            //此处GETSET为原子性操作（类似抛绣球），若两个线程AB同时到这里，只有一个线程能够获取到旧锁的到期时间(假设为A),
            //此时B获取到的值是旧锁的到期时间+A锁的到期时间
            //下面的判断只有A锁满足条件，但是A的到期时间会被B的到期时间所覆盖，因为时间相差很少，所以一般可以接受
            if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
                locked = true;
                return true;
            }
        }
        return false;
    }


//
//    /***
//     * 抢购代码
//     * @return
//     */
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public Boolean miaosha(Long goodsId) {
//        try {
//            if(n<=0) {
//                System.out.println("手慢拍大腿,抢光啦222222222！");
//                return false;
//            }
//            if (this.lock()) {
//                //修改库存
//                if(n-1>=0) {
//                    n--;
//                    System.out.println("库存数量:"+n+"     成功!!!"+Thread.currentThread().getName() + "获得了锁");
//                 }else {
//                    System.out.println("手慢拍大腿,抢光啦！");
//                    return false;
//                }
//                this.unlock();
//                return true;
//            }else {
//                System.out.println("换个姿势再试试");
//            }
//
//
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        } finally {
//            // 为了让分布式锁的算法更稳键些，持有锁的客户端在解锁之前应该再检查一次自己的锁是否已经超时，再去做DEL操作，因为可能客户端因为某个耗时的操作而挂起，
//            // 操作完的时候锁因为超时已经被别人获得，这时就不必解锁了。 ————这里没有做
//            this.unlock();
//        }
//        return false;
//    }






    /**
     * Acqurired lock release.
     */
    public synchronized void unlock() {
        if (locked) {
            redisService.del(lockKey);
            locked = false;
        }

    }
}
